package tech.waterism.utils.method;


import tech.waterism.dto.DSpillwaysDto;
import tech.waterism.entity.DZqSpillway;
import tech.waterism.entity.DZqSpillwayH;
import tech.waterism.utils.MathFun;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 水库调节，求泄流量
 */
public class ResOutFlow {

    /**
     * 求一水位时的全部最大泄量
     *
     * @param z 当前水位
     * @return 全部最大泄量
     */
    public static double outflowCapacity(double z, List<DSpillwaysDto> spillways) {
        // todo
        double qMax = 0;
        // 求每一泄流设备在水位为z时的最大泄流量
        if (spillways != null && spillways.size() > 0) {
            for (int j = 0; j < spillways.size(); j++) {
                qMax += outflowFromOneSpillway(z, new Integer[spillways.get(j).getNopenstates()], spillways.get(j),
                        true);
            }
            //丰满需要定制, 最大泄流不超过7500, 机组参与泄流
            //            if (idComp.equals(Constants.ID_COMP_FENGMAN)
            //                    || idComp.equals(Constants.STATION_CODE_FENGMAN)) {
            //                if (qMax >= 7500) {
            //                    for (int i = 0; i < this.nSpillWays; i++) {
            //                        qMax += outflowFromOneSpillwayFM(z, spillWay[i], true);
            //                    }
            //                }
            //            }
        } else {
            qMax = 99999999;
        }
        return qMax;
    }

    /**
     * 求每一泄流设备在水位为z时的泄流量或最大泄流量（公式法或三点插值）
     *
     * @param z          当前水位
     * @param nUsedGates 使用的设备数
     * @param spillWay   SpillWay数据结构
     * @param bMax       bMax=true: 求泄流能力，即全开所有闸门
     * @return 泄流量或最大泄流量（公式法或三点插值）
     */
    private static double outflowFromOneSpillway(double z, Integer[] nUsedGates, DSpillwaysDto spillWay, boolean bMax) {
        double qOut = 0;
        if (bMax) { //
            for (int iState = 0; iState < spillWay.getNopenstates(); iState++) { // 让全开的泄流设备数最大
                if (spillWay.getZqMethods().get(iState).getStatusid() == spillWay.getZqMethods().size()) {
                    nUsedGates[iState] = spillWay.getNgates();
                } else {
                    nUsedGates[iState] = 0;
                }
            }
        }
        for (int iState = 0; iState < spillWay.getNopenstates(); iState++) {
            if (nUsedGates[iState] > 0) {
                if (spillWay.getZclose() == -1) {
                    if (z > spillWay.getZcrest() && z > spillWay.getZopen()) {
                        if (spillWay.getZqMethods().get(iState).getCalmethod() == 2) { // 判别用公式还是用3点插值求泄量
                            qOut = qOut +
                                    outflowFromOneByFormula(z, spillWay, iState) * nUsedGates[iState]; // ??每种形式的开度是否都相同
                        } else {
                            qOut = qOut + MathFun.insert2(z,
                                    spillWay.getZqMethods().get(iState).getZqSpillways().stream()
                                            .mapToDouble(DZqSpillway::getZ).toArray(),
                                    spillWay.getZqMethods().get(iState).getZqSpillways().stream()
                                            .mapToDouble(DZqSpillway::getQ).toArray(),
                                    spillWay.getZqMethods().get(iState).getZqSpillways().size()) * nUsedGates[iState];
                        }
                    }
                } else {
                    if (z > spillWay.getZcrest() && z > spillWay.getZopen() && z < spillWay.getZclose()) {
                        if (spillWay.getZqMethods().get(iState).getCalmethod() == 2) { // 判别用公式还是用3点插值求泄量
                            qOut = qOut +
                                    outflowFromOneByFormula(z, spillWay, iState) * nUsedGates[iState]; // ??每种形式的开度是否都相同
                        } else {
                            qOut = qOut + MathFun.insert3(z,
                                    spillWay.getZqMethods().get(iState).getZqSpillways().stream()
                                            .mapToDouble(DZqSpillway::getZ).toArray(),
                                    spillWay.getZqMethods().get(iState).getZqSpillways().stream()
                                            .mapToDouble(DZqSpillway::getQ).toArray(),
                                    spillWay.getZqMethods().get(iState).getZqSpillways().size()) * nUsedGates[iState];
                        }
                    }
                }
            }
        }
        return qOut;
    }

    /**
     * 求每一泄流设备在水位为z时用公式法求的泄流量
     *
     * @param z        当前水位
     * @param spillway 泄流设备
     * @param iState   第i个开启状态
     * @return 公式法求得的泄流量
     */
    private static double outflowFromOneByFormula(double z, DSpillwaysDto spillway,
                                                  int iState) {// 参数意义:z为水位、aSpillWay为DS_SpillWay数据结构
        double qOut = 0;
        double h0 = z - spillway.getZcrest();
        qOut = spillway.getZqMethods().get(iState).getMflow() * spillway.getZqMethods().get(iState).getGatewidth() *
                Math.pow(h0, 1.5);
        return qOut;
    }

    public static double getOutFlowH(double z, double h, Integer[][] nUsedGates, List<DSpillwaysDto> spillways) {
        double qOut = 0;
        for (int j = 0; j < spillways.size(); j++) {
            if (h > 0) {
                qOut += outflowHFromOneSpillway(z, h, nUsedGates[j], spillways.get(j), false);
            }
        }
        return qOut;
    }

    /**
     * 求分洪设备在水位为z时,水位差为h时的泄流量或最大泄流量
     */
    public static double outflowHFromOneSpillway(double z, double h, Integer[] nUsedGates, DSpillwaysDto spillWay,
                                                 boolean bMax) {
        double qOut = 0;
        if (bMax) { //
            for (int iState = 0; iState < spillWay.getNopenstates(); iState++) { // 让全开的泄流设备数最大
                if (spillWay.getZqMethods().get(iState).getStatusid() == spillWay.getZqMethods().size()) {
                    nUsedGates[iState] = spillWay.getNgates();
                } else {
                    nUsedGates[iState] = 0;
                }
            }
        }
        for (int iState = 0; iState < spillWay.getNopenstates(); iState++) {
            if (nUsedGates[iState] > 0) {
                if (spillWay.getZclose() == -1) {
                    if (z > spillWay.getZcrest()) {
                        List<DZqSpillwayH> zqSpillwayHs = spillWay.getZqMethods().get(iState).getZqSpillwayHs();
                        Map<Double, List<DZqSpillwayH>> groupByZ =
                                zqSpillwayHs.stream().collect(Collectors.groupingBy(DZqSpillwayH::getZ));
                        List<DZqSpillwayH> list1 = new ArrayList<>();
                        List<DZqSpillwayH> list2 = new ArrayList<>();
                        double z1 = 0.0;
                        double z2 = 0.0;
                        for (Map.Entry<Double, List<DZqSpillwayH>> entryByZ : groupByZ.entrySet()) {
                            Double zz = entryByZ.getKey();
                            List<DZqSpillwayH> listByZ = entryByZ.getValue();
                            if (z >= zz) {
                                list1 = listByZ;
                                z1 = zz;
                            } else {
                                list2 = listByZ;
                                z2 = zz;
                                break;
                            }
                        }
                        double q1 = MathFun.insert2(h, list1.stream().mapToDouble(DZqSpillwayH::getH).toArray(),
                                list1.stream().mapToDouble(DZqSpillwayH::getQ).toArray(), list1.size()) *
                                nUsedGates[iState];
                        if (list2.size() > 0) {
                            double q2 = MathFun.insert2(h, list2.stream().mapToDouble(DZqSpillwayH::getH).toArray(),
                                    list2.stream().mapToDouble(DZqSpillwayH::getQ).toArray(), list2.size()) *
                                    nUsedGates[iState];
                            qOut = qOut + q1 + (z - z1) / (z2 - z1) * (q2 - q1);
                        } else {
                            qOut = qOut + q1;
                        }
                    }
                }
            }
        }
        return qOut;
    }

    /**
     * 求水位为z时的全部泄流量
     *
     * @param z 当前水位
     * @return 计算的全部泄流量
     */
    public static double getOutflow(double z, Integer[][] nUsedGates, List<DSpillwaysDto> spillways) {
        double qOut = 0;
        for (int j = 0; j < spillways.size(); j++) {
            qOut += outflowFromOneSpillway(z, nUsedGates[j], spillways.get(j), false);
        }
        return qOut;
    }

    /**
     * 求水位为z时最大下泄能力
     *
     * @param z    当前水位
     * @param qOut 最大出流量
     * @return 计算的最大下泄能力
     */
    public static double checkOutflow(double z, double qOut, List<DSpillwaysDto> spillways) {
        double qCapacity = outflowCapacity(z, spillways);
        if (qOut > qCapacity) {
            qOut = qCapacity;
        }
        return qOut;
    }

    public static double checkOutflowH(double z, double h, double qOut, List<DSpillwaysDto> spillways) {
        double qCapacity = outflowCapacityH(z, h, spillways);
        if (qOut > qCapacity) {
            qOut = qCapacity;
        }
        return qOut;
    }

    public static double outflowCapacityH(double z, double h, List<DSpillwaysDto> spillways) {
        double qMax = 0;
        // 求每一泄流设备在水位为z时的最大泄流量

        if (spillways != null && spillways.size() > 0) {
            for (int j = 0; j < spillways.size(); j++) {
                qMax += outflowHFromOneSpillway(z, h, new Integer[spillways.get(j).getNopenstates()], spillways.get(j),
                        true);
            }
        } else {
            qMax = 99999999;
        }

        return qMax;
    }

    /**
     * 丰满定制, 泄流大于等于7500, 最大泄流采用该函数求解
     *
     * @param z        当前水位
     * @param spillWay SpillWay数据结构
     * @param bMax     bMax=true: 求泄流能力，即全开所有闸门
     * @return 泄流量或最大泄流量（公式法或三点插值）
     */
    private static double outflowFromOneSpillwayFM(double z, DSpillwaysDto spillWay, boolean bMax) {
        double qOut = 0;
        int nUsedGates[] = new int[spillWay.getNopenstates()];
        if (bMax) { //
            for (int iState = 0; iState < spillWay.getNopenstates(); iState++) { // 让全开的泄流设备数最大
                if (spillWay.getZqMethods().get(iState).getStatusid() == spillWay.getZqMethods().size()) {
                    nUsedGates[iState] = spillWay.getNgates();
                } else {
                    nUsedGates[iState] = 0;
                }
                if (spillWay.getSpillwayid() == 3) {
                    nUsedGates[iState] = 0;
                }
            }
        }
        for (int iState = 0; iState < spillWay.getNopenstates(); iState++) {
            if (nUsedGates[iState] > 0) {
                if (spillWay.getZclose() == -1) {
                    if (z > spillWay.getZcrest() && z > spillWay.getZopen()) {
                        if (spillWay.getZqMethods().get(iState).getCalmethod() == 2) { // 判别用公式还是用3点插值求泄量
                            qOut = qOut +
                                    outflowFromOneByFormula(z, spillWay, iState) * nUsedGates[iState]; // ??每种形式的开度是否都相同
                        } else {
                            qOut = qOut + MathFun.insert3(z,
                                    spillWay.getZqMethods().get(iState).getZqSpillways().stream()
                                            .mapToDouble(DZqSpillway::getZ).toArray(),
                                    spillWay.getZqMethods().get(iState).getZqSpillways().stream()
                                            .mapToDouble(DZqSpillway::getQ).toArray(),
                                    spillWay.getZqMethods().get(iState).getZqSpillways().size()) * nUsedGates[iState];
                        }
                    }
                } else {
                    if (z > spillWay.getZcrest() && z > spillWay.getZopen() && z < spillWay.getZclose()) {
                        if (spillWay.getZqMethods().get(iState).getCalmethod() == 2) { // 判别用公式还是用3点插值求泄量
                            qOut = qOut +
                                    outflowFromOneByFormula(z, spillWay, iState) * nUsedGates[iState]; // ??每种形式的开度是否都相同
                        } else {
                            qOut = qOut + MathFun.insert3(z,
                                    spillWay.getZqMethods().get(iState).getZqSpillways().stream()
                                            .mapToDouble(DZqSpillway::getZ).toArray(),
                                    spillWay.getZqMethods().get(iState).getZqSpillways().stream()
                                            .mapToDouble(DZqSpillway::getQ).toArray(),
                                    spillWay.getZqMethods().get(iState).getZqSpillways().size()) * nUsedGates[iState];
                        }
                    }
                }
            }
        }
        return qOut;
    }
}
